import json
import dateutil.parser
import google.protobuf.json_format as json_format
from cc.appcoin2.support.order.py_parser import PyOrderParser
from coin.exchange.ftx.kr_rest.private_client import FTXPrivateParser
from coin.exchange.ftx_futures.kr_rest.native_private_client import FTXFuturesNativePrivateClient
from coin.exchange.ftx_futures.kr_rest.futures_product import FTXFuturesProduct
from coin.exchange.ftx.kr_rest.currency import FTXCurrency
import coin.exchange.base.kr_rest.private_client_base as privcb
from coin.proto.coin_query_pb2 import (
                                        AccountPosition, 
                                        ProductPosition,
                                        CurrencyTransferElement,
                                        CurrencyTransferBundle,
                                        AccountBalance)
from coin.exchange.ftx_futures.kr_rest.product_info import ftx_futures_product_info
from coin2.exchange.order.system_pb2 import OrderSystemConfig
from coin2.exchange.symbology_pb2 import ProductConfig


class FTXFuturesPrivateParser(object):
  @staticmethod
  def parse_account_balance(update_msg):
    return FTXPrivateParser.parse_account_balance(update_msg)

  @staticmethod
  def parse_account_position(update_msg):
    """
    {
      "success": true,
      "result": [
        {
          "cost": -31.7906,
          "entryPrice": 138.22,
          "estimatedLiquidationPrice": 152.1,
          "future": "ETH-PERP",
          "initialMarginRequirement": 0.1,
          "longOrderSize": 1744.55,
          "maintenanceMarginRequirement": 0.04,
          "netSize": -0.23,
          "openSize": 1744.32,
          "realizedPnl": 3.39441714,
          "shortOrderSize": 1732.09,
          "side": "sell",
          "size": 0.23,
          "unrealizedPnl": 0
        }
      ]
    }
    """
    assert update_msg['success'] == True, update_msg
    product_positions = {}
    for pos_info in update_msg['result']:
      native_symbol = pos_info['future']
      product = FTXFuturesProduct.FromStrNativeProduct(native_symbol)
      position = product_positions.get(native_symbol)
      if position is None:
        position = ProductPosition(symbol=product.symbol)
        product_positions[native_symbol] = position
      if pos_info['side'] == 'buy':
        position.long_position = float(pos_info['size'])
      elif pos_info['side'] == 'sell':
        position.short_position = -float(pos_info['size'])
      else:
        raise ValueError('Invalid direction!')

    for position in product_positions.values():
      position.net_position = position.long_position + position.short_position

    return AccountPosition(exchange='Ftx', each_position=product_positions.values())

  @staticmethod
  def parse_transfers(withdraw_update_msg, deposite_update_msg, currency):
      """
      Status of withdrawal: "requested", "processing", "complete", or "cancelled"
      """
      withdraw_status_map = {
        'requested': 'TRANSFER_ACCEPTED',
        'processing': 'TRANSFER_ACCEPTED',
        'cancelled': 'CANCEL_CONFIRMED',
        'complete': 'TRANSFER_CONFIRMED'
      }
      """
      Status of deposit
      "confirmed", "unconfirmed", or "cancelled"
      """
      deposite_status_map = {
        'unconfirmed': 'TRANSFER_ACCEPTED',
        'confirmed': 'TRANSFER_CONFIRMED',
        'cancelled': 'CANCEL_CONFIRMED'
      }
      transfer_list = []
      assert withdraw_update_msg['success'], withdraw_update_msg
      assert deposite_update_msg['success'], deposite_update_msg
      for transfer_elem in withdraw_update_msg['result']:
        if transfer_elem.get('txid') and transfer_elem['coin'] == currency.native_currency:
          transfer = CurrencyTransferElement(
              currency=currency.currency,
              type='WITHDRAW',
              to_address=transfer_elem['address'],
              qty=float(transfer_elem['size']),
              fee=float(transfer_elem['fee']),
              external_transfer_id=transfer_elem['txid'],
              status=withdraw_status_map[transfer_elem['status']],
              create_timestamp=int(dateutil.parser.parse(transfer_elem['time']).timestamp() * 1e9)
            )
          transfer_list.append(transfer)
      for transfer_elem in deposite_update_msg['result']:
        if transfer_elem.get('txid') and transfer_elem['coin'] == currency.native_currency:
          transfer = CurrencyTransferElement(
              currency=currency.currency,
              type='DEPOSIT',
              from_address=transfer_elem['address']['address'],
              qty=float(transfer_elem['size']),
              fee=float(transfer_elem.get('fee')),
              external_transfer_id=transfer_elem['txid'],
              status=deposite_status_map[transfer_elem['status']],
              create_timestamp=int(dateutil.parser.parse(transfer_elem['time']).timestamp() * 1e9)
            )
          transfer_list.append(transfer)
      return CurrencyTransferBundle(exchange='Ftx',
                                    market_type='Futures',
                                    currency=currency.currency,
                                    native_currency=currency.native_currency,
                                    each_transfer=transfer_list)
                                  
class FTXFuturesPrivateClient(privcb.PrivateClientBase):
  def __init__(self, key_file, timeout=20):
    privcb.PrivateClientBase.__init__(self)
    self.nprivc = FTXFuturesNativePrivateClient(key_file=key_file, timeout=timeout)
    pi_bundle = ftx_futures_product_info._product_infos
    assert pi_bundle.HasField('mea')
    oss_config = OrderSystemConfig(
      mea=pi_bundle.mea,
      products=ProductConfig(norms=[pi.symbol for pi in pi_bundle.product_infos]))
    self._parser = PyOrderParser.from_mea_str(pi_bundle.mea)
    self._parser.add_product(oss_config.SerializeToString())

  def query_account_balance_impl(self):
    update = self.nprivc.query_account_balance()
    msg = self._parser.parse_account_balance(json.dumps(update.msg))
    account_balance = AccountBalance()
    json_format.Parse(msg, account_balance)
    update.msg = account_balance
    return update

  def query_account_position_impl(self):
    update = self.nprivc.query_account_position()
    msg = self._parser.parse_account_position(json.dumps(update.msg))
    account_position = AccountPosition()
    json_format.Parse(msg, account_position)
    update.msg = account_position
    # print(update.msg)
    return update
  
  def query_account_transfer_impl(self, currency):
    currency = FTXCurrency.FromStr(currency)
    withdraw_update = self.nprivc.query_withdraw_history()
    deposit_update = self.nprivc.query_deposit_history()
    update = deposit_update
    update.msg = FTXFuturesPrivateParser.parse_transfers(withdraw_update.msg,
                                                   deposit_update.msg,
                                                   currency)
    return update

  def query_list_orders_impl(self, product):
    return NotImplementedError

  def query_cancel_product_impl(self, product):
    raise NotImplementedError

  def query_fills_impl(self, product):
    raise NotImplementedError

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError
